package com.yunban.project.api.text_check.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yunban.project.api.text_check.domain.WordParseRecord;
import com.yunban.project.api.text_check.dto.deepseek.ChatMessage;
import com.yunban.project.api.text_check.dto.deepseek.ChatReq;
import com.yunban.project.api.text_check.dto.deepseek.ChatResp;
import com.yunban.project.api.text_check.dto.deepseek.enums.DeepSeekModel;
import com.yunban.project.api.text_check.mapper.WordParseRecordMapper;
import com.yunban.project.api.text_check.service.DeepSeekService;
import com.yunban.project.api.text_check.utils.DeepSeekApplication;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.base.common.exception.BusinessException;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Flux;

import java.io.File;
import java.io.FileOutputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author: lijiajun
 * @date: 2025-02-27
 * @version: 1.0
 */
@Slf4j
@Service
public class DeepSeekServiceImpl implements DeepSeekService {

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private WordParseRecordMapper wordParseRecordMapper;

    String apiUrl = "https://api.deepseek.com/chat/completions";
    String apiKey = "sk-bbb5a03b03de4358b9ac23069bbf01c6";

    public static void main(String[] args) {
        DeepSeekServiceImpl deepSeekService = new DeepSeekServiceImpl();
        ChatReq req = new ChatReq();
        req.setModel(DeepSeekModel.DEEPSEEK_V3.getModel());
        String prompt = "#角色：语句分析与纠错专家\n" +
                "\n" +
                "#风格特点:\n" +
                "1. 采用五维文本问题分类框架（新增错误等级维度），涵盖固定用法、标点符号、语法错误、敏感词和标准用法\n" +
                "2. 专注执行文本基础层面的校对工作，具备逐字核对的严谨性 \n" +
                "3. 错误等级判定标准：\n" +
                "    3.1. 一级错误（最严重）：\n" +
                "        - 字词错误（错别字、核心数据错误、专有名词错误）\n" +
                "            - 错别字（例：将“你好”误为“你号”）\n" +
                "            - 专有名词错误（例：将“北京”误为“背景”）\n" +
                "        - 严重标点符号错误（连续重复标点、标点缺失导致歧义）\n" +
                "            - 连续重复标点（例：连续两个逗号“，，”或句号“。。”）\n" +
                "            - 标点符号缺失导致语义歧义（例：“我们去吃饭吧他同意了”）\n" +
                "            - 标点符号错用导致逻辑混乱（例：将句号误为逗号，导致句子无法结束）\n" +
                "        - 敏感词错误（政治敏感词、侮辱性词汇）\n" +
                "            - 政治敏感词、侮辱性词汇等不符合规范的表达。\n" +
                "        - 语法结构错误（句子成分缺失或冗余）\n" +
                "            - 句子成分缺失或冗余，导致语义无法理解（例：“我吃饭了昨天”）。\n" +
                "    3.2. 二级错误（一般）：\n" +
                "        - 固定搭配错误（的、地、得不分，量词错误）\n" +
                "            - “的、地、得”使用错误（例：“高兴得跳起来”误为“高兴的跳起来”\n" +
                "            - 量词错误（例：“一本书”误为“一个书”）\n" +
                "        - 轻微标点符号错误（标点不规范但不影响语义）\n" +
                "            - 标点符号使用不规范但不影响语义（例：中文中使用英文标点“,”或“.”\n" +
                "        - 语义表达不准确（用词不当但语义可推测）\n" +
                "            - 用词不当但语义可推测（例：“他非常生气”误为“他非常生气气”。\n" +
                "        - 专有名称错误\n" +
                "            - 例如：“12345”热线 误为 “1234”热线，“习近平总书记” 误为 “习大大”\n" +
                "    3.3. 三级错误（建议性修改）：\n" +
                "        - 建议性修改（用词优化、句式优化）\n" +
                "            - “进行改革”建议改为“实施改革”）\n" +
                "            - “因为天气很好，所以我们去公园”建议改为“天气很好，我们去公园”\n" +
                "        - 标点符号建议性修改（标点不规范但语义清晰）\n" +
                "            - 标点符号使用不够规范但语义清晰（例：长句中缺少逗号分隔，但不影响理解）\n" +
                "            - 结尾增加结尾标点符号（例：“我们去公园”建议改为“我们去公园。”）\n" +
                "4. 遵循严格的格式要求，执行标点符号转换时仅处理明确指定的类型\n" +
                "5. 纠正错误时最大限度保留原文结构和表达意图\n" +
                "6. 不返回除了纠错文本以外的任何内容\n" +
                "7. 输出结果严格遵循输出格式项\n" +
                "8. 不添加未包含的标点符号，严格保持原文本段落结构\n" +
                "9. 在输出内容中也要体现这个纠错的参考文献来源来自于哪里\n" +
                "\n" +
                "#专有名词校验规则\n" +
                "    - 针对行政区划名称错误，需严格按照《中华人民共和国行政区划手册》进行纠错：\n" +
                "        省级行政区（如“浙江省”）不可与市级行政区（如“杭州市”）混淆。\n" +
                "        市级行政区（如“上海市”）不可与区级行政区（如“浦东新区”）混淆。\n" +
                "    - 示例：\n" +
                "        错误：“浦东市团委” → 正确：“浦东新区团委”\n" +
                "        错误：“浙江市团委” → 正确：“浙江省团委”\n" +
                "    - 针对专有名词错误，需严格按照官方文件或权威资料进行纠错：\n" +
                "        示例：\n" +
                "        错误：“1234热线” → 正确：“12345热线”\n" +
                "        错误：“北京大学” → 正确：“北京大学”\n" +
                "\n" +
                "#能力限制:\n" +
                "1. 不处理文档排版、字体格式等非文本内容\n" +
                "2. 不添加原文未包含的标点符号\n" +
                "3. 不提供文本润色或风格优化服务\n" +
                "4. 对文本中错误的标点符号的使用也要进行纠错\n" +
                "5. 不处理超过5000字的批量文本任务\n" +
                "6. 严禁在标点符号后面再加上标点符号\n" +
                "7. 如果可能存在专有名词，则专有名词完全不可以修改任何字符、符号\n" +
                "8. 不要做一些无意义的删除\n" +
                "9. 在处理引号时，必须保持引号的完整性。\n" +
                "10. 级别严格按照“错误等级判定标准”来输出，并且不能是阿拉伯数字\n" +
                "\n" +
                "\n" +
                "#输出格式\n" +
                "输出：xxxxxx 分类：xxxx 错误等级：x级 来源：来源于哪个参考文献/书\n" +
                "\n" +
                "\n" +
                "需要进行纠错的文本：“美方做法严重违反世贸组织规则，严重损害以规则为基础的多边贸易体制，无益于解决问题。中方将采取一切必要措施维护自身合法权益。”习大大说。";
        req.setMessage(prompt);
        System.out.println(deepSeekService.chat(req));
    }

    @Override
    public String chat(ChatReq req) {
        // 构建请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(apiKey);
        log.info("DEEPSEEK API请求参数：{}", JSON.toJSONString(req));
        // 构造请求体
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("model", req.getModel());
        requestBody.put("messages", Collections.singletonList(
                Map.of("role", "user", "content", req.getMessage())
        ));
        requestBody.put("stream", false);
        try {
            ResponseEntity<ChatResp> response = this.restTemplate.exchange(
                    apiUrl,
                    HttpMethod.POST,
                    new HttpEntity<>(requestBody, headers),
                    ChatResp.class
            );
            log.info("DEEPSEEK API请求结果1：{}", JSON.toJSONString(response));
            // 解析响应结果
            if (response.getStatusCode().is2xxSuccessful()) {
                ChatResp chatResp = response.getBody();
                log.info("DEEPSEEK API请求结果2：{}", JSON.toJSONString(chatResp));
                if (ObjectUtils.isEmpty(chatResp)) {
                    return null;
                }
                List<ChatResp.Choice> choices = chatResp.getChoices();
                log.info("DEEPSEEK API请求结果3：{}", JSON.toJSONString(choices));
                if (!CollectionUtils.isEmpty(choices)) {
                    ChatMessage message = choices.get(0).getMessage();
                    if (!ObjectUtils.isEmpty(message)) {
                        return message.getContent();
                    }
                }
            }
            throw new BusinessException("DEEPSEEK API请求失败");
        } catch (HttpStatusCodeException e) {
            log.error("DEEPSEEK API请求失败，错误信息：{}", e.getMessage());
            throw new BusinessException("DEEPSEEK API请求失败:" + e.getMessage());
        }
    }

    WordParseRecord saveWordParse(String filename) {
        WordParseRecord r = new WordParseRecord();
        r.setTaskId(UUID.randomUUID().toString());
        r.setFilename(filename);
        this.wordParseRecordMapper.insert(r);
        return r;
    }

    @Override
    public String word2Markdown(MultipartFile file) {
//        // 调用 http://192.168.1.88/web-api/docx_to_markdown 上传文件路径，获取数据
//        String doc2markdownApi = "http://192.168.1.88:5000/web-api/docx_to_markdown";
//        // 图片存放目录
//        String filePath = "E://python-project/word-decode/temp/markdown";
//        HttpHeaders headers = new HttpHeaders();
//        headers.setContentType(MediaType.APPLICATION_JSON);
//        DeepSeekApplication application = new DeepSeekApplication();
//        DeepSeekApplication.FileInfo fileInfo = application.getFileExt(file);
//        if (!"docx".equals(fileInfo.getExt())) {
//            throw new BusinessException("目前仅支持docx格式");
//        }
//        File tempFile = null;
//        try {
//            // 将 MultipartFile 转换为临时文件
//            tempFile = File.createTempFile("upload", "." + fileInfo.getExt());
//            try (FileOutputStream fos = new FileOutputStream(tempFile)) {
//                fos.write(file.getBytes());
//            }
//            // 创建请求体
//            System.out.println(tempFile.getPath());
//            Map<String, String> requestBody = new HashMap<>();
//            requestBody.put("docx_path", tempFile.getPath());
//            requestBody.put("output_dir", filePath);
//            HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
//            // 发送POST请求并接收响应
//            ResponseEntity<String> response = this.restTemplate.exchange(
//                    doc2markdownApi,
//                    HttpMethod.POST,
//                    requestEntity,
//                    String.class);
//            if (ObjectUtils.isEmpty(response)) {
//                throw new BusinessException("doc2markdown error: response is null");
//            }
//            if (response.getStatusCode() == HttpStatus.OK) {
//                return response.getBody();
//            } else {
//                throw new BusinessException("Failed to upload file. HTTP Status Code: " + response.getStatusCodeValue());
//            }
//        } catch (Exception ex) {
//            log.error("doc2markdown error", ex);
//            throw new BusinessException("doc2markdown error:" + ex.getMessage());
//        }
        return null;
    }

    @Override
    public String tencentSSEChat(String markdownStr, String filename) {
        // PromptWord
//        String promptWord = markdownStr + "\n" + "#能力限制:\n" +
//                "\n" +
//                "- 将上述markdown格式转换为微信推文HTML代码\n" +
//                "- 结构要求：严格保持原文段落顺序，保留所有文字表述（含全角/半角符号、空格、换行），禁止任何内容改写或格式简化。\n" +
//                "- 图片处理规则，图片居中显示，左右可适当留白。\n" +
//                "- 分析内容，可以对某些部分文字做相应样式上的处理，例如标题、落款等等。\n" +
//                "- 样式规范：按照微信推文的样式进行排版，排版需好看，排版风格根据推文内容自行创作，例如科普风格。\n" +
//                "- 生成响应式布局适配手机端，添加头图：https://st-file.yunbanos.cn/uploadsoss/file/2025-02-26/9c03861b44c10aaf543dbe97c81a59b6.png。 \n" +
//                "- 头图左右撑满无需留白，其他图片左右居中。 \n" +
//                "- 推文背景使用：#FF0000 \n" +
//                "- 字体以及颜色、图片边框、小标题样式可根据主色调进行调整。 \n" +
//                "- 输出可直接复制到微信公众号的完整HTML代码。\n" +
//                "\n" +
//                "#输出要求\n" +
//                "\n" +
//                "- 绝对禁止对文本内容进行任何形式的二次创作或内容补充\n" +
//                "- 不提供文本润色的服务\n" +
//                "- 只对与上方markdown文本生成对应的HTML代码\n" +
//                "- 对于输出内容里的字符不要做特殊字符转译";
//        // TODO 这个代码暂时只是为了测试验证，后续需要进行优化
//        WordParseRecord parseRecord = this.saveWordParse(filename);
//        TencentSSEChatReq tencentSSEChatReq = new TencentSSEChatReq(promptWord, "hlHDsZMo");
//        // 创建 WebClient 实例
//        WebClient webClient = WebClient.create();
//        // 发送 POST 请求并处理 SSE 流
//        Flux<String> sseStream = webClient.post()
//                .uri("https://wss.lke.cloud.tencent.com/v1/qbot/chat/sse")
//                .contentType(MediaType.APPLICATION_JSON)
//                .bodyValue(tencentSSEChatReq)
//                // 接收 SSE 流
//                .accept(MediaType.TEXT_EVENT_STREAM)
//                .retrieve()
//                // 将响应体转换为 Flux<String>
//                .bodyToFlux(String.class);
//        // 订阅 SSE 流并处理每一条消息
//        AtomicReference<String> response = new AtomicReference<>("");
//        AtomicReference<String> htmlResult = new AtomicReference<>("");
//        sseStream.subscribe(
//                data -> {
//                    try {
//                        JSONObject sseJson = JSON.parseObject(data);
//                        JSONObject payloadJson = JSON.parseObject(JSON.toJSONString(sseJson.get("payload")));
//                        // 判断 payloadJson.get("is_final"); 是否是 true
//                        Object isFinalObj = payloadJson.get("is_final");
//                        if (ObjectUtils.isEmpty(isFinalObj)) {
//                            return;
//                        }
//                        boolean isFinal = (isFinalObj instanceof Boolean) ? (Boolean) isFinalObj : Boolean.parseBoolean(isFinalObj.toString());
//                        if (isFinal && "reply".equals(sseJson.get("type").toString())) {
//                            String html = payloadJson.get("content").toString();
//                            // 将 html 中的 \u003c  转为 <  TODO 响应的内容中存在转义字符
//                            html = html.replaceAll("\\u003c", "<");
//                            html = html.replaceAll("\\\\u003c", "<");
//                            html = html.replaceAll("\\u003e", ">");
//                            html = html.replaceAll("\\\\u003e", ">");
//                            // 如果 html 前面是 ```html  或者 后面是  ```  那么则剔除
//                            if (html.startsWith("```html") && html.endsWith("```")) {
//                                html = html.substring(7, html.length() - 3);
//                            }
//                            html = html.trim();
//                            htmlResult.set(html);
//                            response.set(data);
//                        }
//                    } catch (Exception ex) {
//                        log.error("Received SSE data error:{}", ex.getMessage());
//                        log.error("Received SSE data error:{}", data);
//                    }
//                    log.debug("Received SSE data:{}", data);
//                },
//                error -> {
//                    // 处理错误信息
//                    if (error instanceof WebClientResponseException) {
//                        log.error("HTTP error: " + ((WebClientResponseException) error).getStatusCode());
//                        throw new BusinessException("HTTP error: " + ((WebClientResponseException) error).getStatusCode());
//                    } else {
//                        log.error("Error: " + error.getMessage());
//                        throw new BusinessException("Error: " + error.getMessage());
//                    }
//                },
//                () -> {
//                    parseRecord.setHtml(htmlResult.get());
//                    parseRecord.setResult(response.get());
//                    this.wordParseRecordMapper.updateById(parseRecord);
//                }
//        );
//        return parseRecord.getTaskId();
        return null;
    }

    @Data
    public static class TencentSSEChatReq {
        private String request_id;
        private String content;
        private List<Object> file_infos;
        private String session_id;
        private String bot_app_key;
        private String visitor_biz_id;
        private Map<String, String> custom_variables;
        private int streaming_throttle;

        public TencentSSEChatReq(String content, String bot_app_key) {
            this.request_id = UUID.randomUUID().toString();
            this.session_id = UUID.randomUUID().toString();
            this.visitor_biz_id = UUID.randomUUID().toString();
            this.content = content;
            this.bot_app_key = bot_app_key;
            HashMap<String, String> customVariableMap = new HashMap<>();
            customVariableMap.put("LOCAL_DB_MATCH", "LOCAL_DB_MATCH");
            this.custom_variables = customVariableMap;
        }
    }
}
